package com.cl.ks.service;


import cn.hutool.poi.excel.ExcelReader;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.chlorine.base.util.ExcelUtil;
import com.chlorine.base.util.FileUtil;
import com.chlorine.base.util.StringUtil;
import com.cl.ks.entity.KsDataField;
import com.cl.ks.entity.KsDataType;
import com.cl.ks.entity.KsFlowGraph;
import com.cl.ks.entity.KsRule;
import com.cl.ks.flow.KsFlowGraphHandler;
import com.cl.ks.repository.KsFlowGraphRepository;
import com.cl.ks.utils.DbUtil;
import com.cl.ks.utils.FileUploadSecurity;
import com.cl.ks.utils.QueryDslUtil;
import com.cl.mvc.service.BaseService;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.jpa.impl.JPAQueryFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.persistence.criteria.Order;
import javax.persistence.criteria.Predicate;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

import static com.cl.ks.constant.QTableConstant.*;
import static org.apache.commons.io.FileUtils.deleteDirectory;

@Service
public class KsFlowGraphService extends BaseService<KsFlowGraph> {
    private static final String KS_FLOW_GRAPHS = "ksFlowGraphs";
    private static final String KS_RULES = "ksRules";
    private static final String KS_DATA_TYPES = "ksDataTypes";
    private static final String KS_DATA_TYPE_FIELDS = "ksDataTypeFields";


    private final JPAQueryFactory queryFactory;
    private final KsFlowGraphRepository ksFlowGraphRepository;

    private final KsRuleServiceOld ksRuleService;

    private final KsDataTypeService ksDataTypeService;
    private final KsDataFieldService ksDataFieldService;
    private final DbUtil dbUtil;

    protected KsFlowGraphService(KsFlowGraphRepository KsFlowGraphRepository, JPAQueryFactory queryFactory,
                                 KsFlowGraphRepository ksFlowGraphRepository, KsRuleServiceOld ksRuleService, KsDataTypeService ksDataTypeService, KsDataFieldService ksDataFieldService, DbUtil dbUtil) {
        super(KsFlowGraphRepository);
        this.queryFactory = queryFactory;
        this.ksFlowGraphRepository = ksFlowGraphRepository;
        this.ksRuleService = ksRuleService;
        this.ksDataTypeService = ksDataTypeService;
        this.ksDataFieldService = ksDataFieldService;
        this.dbUtil = dbUtil;
    }


    @Transactional(rollbackFor = Exception.class)
    public void cleanCache(KsFlowGraph ksFlowGraph, String auth) {
        Integer id = ksFlowGraph.getId();
        if (id != null && KsFlowGraphHandler.isKsFlowGraphRunning(id)) {
            throw new RuntimeException("清理失败，流程图运行中");
        }
        String checkpointsDir = "./spark-rdd/" + id + "/";
        try {
            deleteDirectory(new File(checkpointsDir));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        queryFactory.update(qKsFlowGraph).set(qKsFlowGraph.failedNodeCode,"").where(qKsFlowGraph.id.eq(id)).execute();
    }

    @Override
    public KsFlowGraph save(KsFlowGraph ksFlowGraph, String auth) {
        if (ksFlowGraph.getId() != null && KsFlowGraphHandler.isKsFlowGraphRunning(ksFlowGraph.getId())) {
            throw new RuntimeException("保存失败，流程图运行中");
        }
        KsFlowGraph res = super.save(ksFlowGraph, auth);
        return res;
    }

    public void deleteById(Integer id, Boolean deleteCreateTablesFlag) {
        if (deleteCreateTablesFlag == true) {
            Optional<KsFlowGraph> ksFlowGraph = findById(id);
            String createTables = ksFlowGraph.get().getCreateTables();
            if (StringUtils.hasLength(createTables)) {
                String[] split = StringUtils.split(createTables, ",");
                if (split != null) {
                    for (String s : split) {
                        String[] dbCodeAndTableName = StringUtils.split(s, ":");
                        dbUtil.executeSQL(Objects.requireNonNull(dbCodeAndTableName)[0], "drop table if exists " + dbCodeAndTableName[1]);
                    }
                }
            }
        }
        repository.deleteById(id);
    }

    public JSONObject tagsGroupCount(List<String> tags, String auth) {
        JSONObject res = new JSONObject();

        String userId = String.valueOf(jwtUtil.getUserId(auth));
        String roleId = String.valueOf(jwtUtil.getRoleId(auth));
        String depId = String.valueOf(jwtUtil.getDepId(auth));
        BooleanExpression powerExpression = QueryDslUtil.generatePowerExpression(qKsFlowGraph, qKsFlowGraph.power, userId, roleId, depId);
        BooleanExpression tagsExpression = qKsFlowGraph.isNotNull().and(qKsFlowGraph.isNull());
        for (String tag : tags) {
            tagsExpression = tagsExpression.or(Expressions.asString(",").concat(qKsFlowGraph.tags).concat(",").contains(tag));
            res.put(tag, 0);
        }
        List<KsFlowGraph> ksFlowGraphList = queryFactory.selectFrom(qKsFlowGraph).where(powerExpression.and(tagsExpression)).fetch();

        for (KsFlowGraph ksFlowGraph : ksFlowGraphList) {
            String itemTag = "," + ksFlowGraph.getTags() + ",";
            for (String tag : tags) {
                if (itemTag.contains("," + tag + ",")) {
                    res.put(tag, res.getIntValue(tag) + 1);
                }
            }
        }
        return res;

    }

    @Transactional
    public JSONObject exportKsFlowGraphsJson(List<Integer> ids, HttpServletResponse response) {
        JSONObject res = new JSONObject();
        List<KsFlowGraph> ksFlowGraphList = ksFlowGraphRepository.findAllById(ids);
        List<String> dataTypeCodeList = new ArrayList<>();
        List<Integer> ruleIdList = new ArrayList<>();
        for (KsFlowGraph ksFlowGraph : ksFlowGraphList) {
//            ksFlowGraph.setKsFlowGraphRecordList(new ArrayList<>());
            String ksDataTypeCodes = ksFlowGraph.getKsDataTypeCodes();
            String ruleIds = ksFlowGraph.getRuleIds();
            List<String> ksDataTypeCodesSplit = StringUtil.split(ksDataTypeCodes, ",");
            List<Integer> ruleIdsSplit = StringUtil.splitIntegers(ruleIds, ",");
            dataTypeCodeList.addAll(ksDataTypeCodesSplit);
            ruleIdList.addAll(ruleIdsSplit);
        }

        res.put(KS_DATA_TYPES, ksDataTypeService.exportKsDataTypes(dataTypeCodeList));
        res.put(KS_DATA_TYPE_FIELDS, ksDataFieldService.exportKsDataFields(dataTypeCodeList));
        res.put(KS_RULES, ksRuleService.exportKsRules(ruleIdList));
        res.put(KS_FLOW_GRAPHS, ksFlowGraphList);
//      FileUtil.exportFile(res.toJSONString(), "result.json", response);

        return res;
    }


    @Transactional
    public void importKsFlowGraphs(MultipartFile file, String auth) throws IOException {
        FileUploadSecurity.validateFile(file);
        String power = "";
        String userId = String.valueOf(jwtUtil.getUserId(auth));
        String roleId = String.valueOf(jwtUtil.getRoleId(auth));
        String depId = String.valueOf(jwtUtil.getDepId(auth));
        if (StringUtils.hasLength(userId) && !userId.equals("null")) {
            power += ",r_" + userId;
        }
        if (StringUtils.hasLength(roleId) && !roleId.equals("null")) {
            power += ",j_" + roleId;
        }
        if (StringUtils.hasLength(depId) && !depId.equals("null")) {
            power += ",b_" + depId;
        }
        List<KsDataType> ksDataTypes = new ArrayList<>();
        List<KsDataField> ksDataTypeFields = new ArrayList<>();
        List<KsRule> ksRuleList = new ArrayList<>();
        List<KsFlowGraph> ksFlowGraphs = new ArrayList<>();
        String filename = file.getOriginalFilename();
        if (filename == null) {
            return;
        }
        if (filename.endsWith(".xlsx")) {
            ExcelReader reader1 = cn.hutool.poi.excel.ExcelUtil.getReader(file.getInputStream(), KS_DATA_TYPES);
            ksDataTypes = reader1.readAll(KsDataType.class);

            ExcelReader reader4 = cn.hutool.poi.excel.ExcelUtil.getReader(file.getInputStream(), KS_DATA_TYPE_FIELDS);
            ksDataTypeFields = reader4.readAll(KsDataField.class);

            ExcelReader reader2 = cn.hutool.poi.excel.ExcelUtil.getReader(file.getInputStream(), KS_RULES);
            ksRuleList = reader2.readAll(KsRule.class);

            ExcelReader reader3 = cn.hutool.poi.excel.ExcelUtil.getReader(file.getInputStream(), KS_FLOW_GRAPHS);
            ksFlowGraphs = reader3.readAll(KsFlowGraph.class);
        }
        if (filename.endsWith(".json")) {
            String s = FileUtil.readFile(file);
            JSONObject data = JSONObject.parseObject(s);
            ksDataTypes = JSONArray.parseArray(data.getString(KS_DATA_TYPES), KsDataType.class);
            ksDataTypeFields = JSONArray.parseArray(data.getString(KS_DATA_TYPE_FIELDS), KsDataField.class);
            ksRuleList = JSONArray.parseArray(data.getString(KS_RULES), KsRule.class);
            ksFlowGraphs = JSONArray.parseArray(data.getString(KS_FLOW_GRAPHS), KsFlowGraph.class);
        }


        String finalPower = power;
        ksDataTypes.forEach(item -> item.setPower(item.getPower() + finalPower));
        ksDataTypeFields.forEach(item -> item.setPower(item.getPower() + finalPower));
        ksRuleList.forEach(item -> {
            item.setPower(item.getPower() + finalPower);
            item.setRuleCode(item.getId() + "." + item.getRuleName());
        });
        ksFlowGraphs.forEach(item -> item.setPower(item.getPower() + finalPower));
        ksDataTypeService.importKsDataTypes(ksDataTypes);
        ksDataFieldService.importKsDataTypeFields(ksDataTypeFields);
        ksRuleService.importKsRules(ksRuleList);
        importKsFlowGraphs(ksFlowGraphs);
    }

    @Transactional
    public void exportKsFlowGraphs(List<Integer> ids, HttpServletResponse response) {
        JSONObject res = new JSONObject();
        List<KsFlowGraph> ksFlowGraphList = ksFlowGraphRepository.findAllById(ids);
        List<String> dataTypeCodeList = new ArrayList<>();
        List<Integer> ruleIdList = new ArrayList<>();
        for (KsFlowGraph ksFlowGraph : ksFlowGraphList) {
//            ksFlowGraph.setKsFlowGraphRecordList(new ArrayList<>());
            String ksDataTypeCodes = ksFlowGraph.getKsDataTypeCodes();
            String ruleIds = ksFlowGraph.getRuleIds();
            List<String> ksDataTypeCodesSplit = StringUtil.split(ksDataTypeCodes, ",");
            List<Integer> ruleIdsSplit = StringUtil.splitIntegers(ruleIds, ",");
            dataTypeCodeList.addAll(ksDataTypeCodesSplit);
            ruleIdList.addAll(ruleIdsSplit);
        }

        res.put(KS_DATA_TYPES, JSONArray.toJSONString(ksDataTypeService.exportKsDataTypes(dataTypeCodeList)));
        res.put(KS_DATA_TYPE_FIELDS, JSONArray.toJSONString(ksDataFieldService.exportKsDataFields(dataTypeCodeList)));
        res.put(KS_RULES, JSONArray.toJSONString(ksRuleService.exportKsRules(ruleIdList)));
        res.put(KS_FLOW_GRAPHS, JSONArray.toJSONString(ksFlowGraphList));
        ExcelUtil.exportExcel(res, "result", response);
    }

    public KsFlowGraph importKsFlowGraph(KsFlowGraph ksFlowGraph) {
        ksFlowGraph.setId(null);
        KsFlowGraph newKsFlowGraph = ksFlowGraphRepository.save(ksFlowGraph);
        return newKsFlowGraph;
    }

    public void importKsFlowGraphsJson(String json, String auth) {
        String power = "";
        String userId = String.valueOf(jwtUtil.getUserId(auth));
        String roleId = String.valueOf(jwtUtil.getRoleId(auth));
        String depId = String.valueOf(jwtUtil.getDepId(auth));
        if (StringUtils.hasLength(userId) && !userId.equals("null")) {
            power += ",r_" + userId;
        }
        if (StringUtils.hasLength(roleId) && !roleId.equals("null")) {
            power += ",j_" + roleId;
        }
        if (StringUtils.hasLength(depId) && !depId.equals("null")) {
            power += ",b_" + depId;
        }
        List<KsDataType> ksDataTypes = new ArrayList<>();
        List<KsDataField> ksDataTypeFields = new ArrayList<>();
        List<KsRule> ksRuleList = new ArrayList<>();
        List<KsFlowGraph> ksFlowGraphs = new ArrayList<>();
        JSONObject data = JSONObject.parseObject(json);
        ksDataTypes = JSONArray.parseArray(data.getString(KS_DATA_TYPES), KsDataType.class);
        ksDataTypeFields = JSONArray.parseArray(data.getString(KS_DATA_TYPE_FIELDS), KsDataField.class);
        ksRuleList = JSONArray.parseArray(data.getString(KS_RULES), KsRule.class);
        ksFlowGraphs = JSONArray.parseArray(data.getString(KS_FLOW_GRAPHS), KsFlowGraph.class);


        String finalPower = power;
        ksDataTypes.forEach(item -> item.setPower(item.getPower() + finalPower));
        ksDataTypeFields.forEach(item -> item.setPower(item.getPower() + finalPower));
        ksRuleList.forEach(item -> {
            item.setPower(item.getPower() + finalPower);
            item.setRuleCode(item.getId() + "." + item.getRuleName());
        });
        ksFlowGraphs.forEach(item -> item.setPower(item.getPower() + finalPower));
        ksDataTypeService.importKsDataTypes(ksDataTypes);
        ksDataFieldService.importKsDataTypeFields(ksDataTypeFields);
        ksRuleService.importKsRules(ksRuleList);
        importKsFlowGraphs(ksFlowGraphs);
    }

    public List<KsFlowGraph> importKsFlowGraphs(List<KsFlowGraph> ksFlowGraphs) {
        List<KsFlowGraph> newKsFlowGraphs = new ArrayList<>();
        ksFlowGraphs.forEach(ksRule -> {
            newKsFlowGraphs.add(importKsFlowGraph(ksRule));
        });
        return newKsFlowGraphs;
    }

    public List<KsFlowGraph> importKsFlowGraphs(String ksFlowGraphsJson) {
        List<KsFlowGraph> newKsFlowGraphs = new ArrayList<>();
        List<KsFlowGraph> ksFlowGraphs = JSONObject.parseArray(ksFlowGraphsJson, KsFlowGraph.class);
        ksFlowGraphs.forEach(ksRule -> {
            newKsFlowGraphs.add(importKsFlowGraph(ksRule));
        });
        return newKsFlowGraphs;
    }

    @Override
    public Page<KsFlowGraph> page(KsFlowGraph entity, String auth) {
        String ruleNameContains = String.valueOf(entity.getParams().get("ruleNameContains"));
        String dataTypeCodeContains = String.valueOf(entity.getParams().get("dataTypeCodeContains"));
        String clueRateSort = String.valueOf(entity.getParams().get("clueRateSort"));

        entity.getParams().remove("ruleNameContains");
        entity.getParams().remove("dataTypeCodeContains");
        entity.getParams().remove("clueRateSort");
        List<Integer> ruleIdList = queryFactory.select(qKsRule.id).from(qKsRule).where(qKsRule.ruleName.contains(ruleNameContains)).fetch();
        List<String> dataTypeCodeList = queryFactory.select(qKsDataType.code).from(qKsDataType).where(qKsDataType.name.contains(dataTypeCodeContains)).fetch();

        List<Order> orderList = new ArrayList<>();
        Specification<KsFlowGraph> specification = (root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicateList = new ArrayList<>();

            List<Predicate> ruleIdPredicates = new ArrayList<>();
            List<Predicate> dataTypeCodePredicates = new ArrayList<>();
            ruleIdList.forEach(ruleId -> {
                ruleIdPredicates.add(criteriaBuilder.like(criteriaBuilder.concat(criteriaBuilder.concat(",", root.get("ruleIds")), ","), "%," + ruleId + ",%"));
            });
            dataTypeCodeList.forEach(dataTypeCode -> {
                dataTypeCodePredicates.add(criteriaBuilder.like(criteriaBuilder.concat(criteriaBuilder.concat(",", root.get("ksDataTypeCodes")), ","), "%," + dataTypeCode + ",%"));
            });
            if (!ruleIdPredicates.isEmpty()) {
                predicateList.add(criteriaBuilder.or(ruleIdPredicates.toArray(new Predicate[0])));
            }
            if (!dataTypeCodePredicates.isEmpty()) {
                predicateList.add(criteriaBuilder.or(dataTypeCodePredicates.toArray(new Predicate[0])));
            }

            if (StringUtils.hasLength(clueRateSort)) {
                if (clueRateSort.contains("desc")) {
                    orderList.add(criteriaBuilder.desc(criteriaBuilder.quot(root.get("ruleDataTotal"), root.get("sourceConfigDataTotal"))));
                } else if (clueRateSort.contains("asc")) {
                    orderList.add(criteriaBuilder.asc(criteriaBuilder.quot(root.get("ruleDataTotal"), root.get("sourceConfigDataTotal"))));
                }
            }
            return criteriaQuery.where(predicateList.toArray(new Predicate[0])).getRestriction();
        };

        return repository.findAll(specification.and(buildSpecification(entity, orderList)).and(buildPowerSpecification(auth)), PageRequest.of(entity.getPageNumber() - 1, entity.getPageSize()));

    }


}
